#include "DirectLOL/graphicElement/curves/bezier.h"

#include <stdio.h>
#include <math.h>


/********************************************************************/
/*                      Constructeur                                */
/********************************************************************/

Bezier::Bezier(int aDiscretisation) : GraphicElement(1)
{
    discretisation=aDiscretisation;
    type=4;
    deCasteljeau=0;
    tDeCasteljeau=0.5;
    InitBuffers();
}

/********************************************************************/
/*                      Méthodes                                    */
/********************************************************************/

void Bezier::InitBuffers()
{
    /*!
     * 	Dans un premier temps, je fais appel à la méthode glGenBuffers qui permet de créer un espace mémoire dans la carte
     * graphique qui sera utilisé pour stocker des informations de différents types (coordonnées du sommet, couleurs, normales
     * etc)
     * 	Chaque espace mémoire ainsi alloué sera identifié par l'entier retourné par cette fonction
     */

    glGenBuffers(1,&vbo->vertexBuffer);
    glGenBuffers(1,&vbo->colorBuffer);


}

void Bezier::FillBuffers()
{
    if(controlVertices.size()>0)  // Il est nécéssaire de vérifier si au moins un point de controle à été initialisé
    {
        /*!
         * 	On utilise glBindBuffer pour définir l'espace mémoire courant.
         * 	Toutes les opérations en rapport avec les buffers modifieront l'espace mémoire courant défini par cette méthode
         */

        glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexBuffer);    // On utilise le buffer contenant les coordonnées spatiales des sommets

        float * vertices= new float[discretisation*3];  //  Initialisation d'un tableau dont la taille est égale au degré
                                                        //  de discrétisation, multiplié par 3 pour chaque axe (x,y,z)

        for(int i=0;i<discretisation*3;i+=3)    //  Je définis les coordonnées de chacun des sommets discrétisant la courbe de
        {                                       //  bézier à partir des points de controles définis par l'utilisateur
            Vectorf*temp=  new Vectorf(0.0, 0.0, 0.0);

            temp=CalculPointBezier((float)i/((float)(discretisation*3.0))); //  Calcul des coordonnées du point discretisant la courbe
                                                                    //  pour t = i/discretisation (compris entre 0 et 1)
            vertices[i]=temp->x();
            vertices[i+1]=temp->y();
            vertices[i+2]=temp->z();
        }

        // 	J'utilise la méthode glBufferData afin de remplir mon espace mémoire
        //	premier argument : le type de buffer, deuxieme, la taille du buffer, 3 eme : les données, 4 eme, voir doc

         glBufferData(GL_ARRAY_BUFFER, sizeof(float)*(discretisation*3), vertices, GL_STREAM_DRAW);

         glBindBuffer(GL_ARRAY_BUFFER, vbo->colorBuffer);    // On utilise le buffer contenant les coordonnées spatiales des sommets

        float * colors= new float[discretisation*3];

         for(int i=0;i<discretisation*3;i+=3)
         {
             colors[i]=1.0;
             colors[i+1]=0.0;
             colors[i+2]=0.0;
         }

         glBufferData(GL_ARRAY_BUFFER,sizeof(float)*discretisation*3.0,colors,GL_STREAM_DRAW);

    }
}

void Bezier::AddControlVertex( Vectorf * aVertex)
{
    controlVertices.push_back(aVertex);
}

void Bezier::Picking()
{

}

void Bezier::SetPicking(unsigned char x, unsigned char y, unsigned char z)
{

}

void Bezier::RenderBuffers()
{
    glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER,vbo->colorBuffer);
    glColorPointer(3, GL_FLOAT, 0, 0);

    /* activation des tableaux de sommets */

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glDrawArrays(GL_LINE_STRIP, 0, discretisation );

    /* desactivation des tableaux de sommet */

    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    glFlush();
}

void Bezier::Display()
{
    glPushMatrix();	//	On utilise PushMatrix afin que toutes les opérations sur la matrice de visualisation restent locales

    glTranslatef(translation->x(),translation->y(),translation->z());

    glRotatef(rotation->x(),1,0,0);
    glRotatef(rotation->y(),0,1,0);
    glRotatef(rotation->z(),0,0,1);

    RenderBuffers();

    glPopMatrix();
}

int Bezier::Factoriel(int a)
{
    int compteur=a;
    for(int i=a-1;i>1;i--)
    {
        compteur=compteur*i;
    }
    if(compteur==0)
    {
        compteur=1;
    }
    return compteur;
}

float Bezier::CalculPolynomeBernstein(int n, int i, float t)
{
    float numerateur    =   (float)Factoriel(n);
    float diviseur      =   (float)(Factoriel(i)*Factoriel(n-i));
    float resultat = numerateur/diviseur;
    float tpowi= pow(t,i);
    float tpowni= pow(1-t, n-i);

    resultat=resultat*tpowi*tpowni;

    return resultat;
}

Vectorf * Bezier::CalculPointBezier(float t)
{
    Vectorf * vertex;
    vertex = new Vectorf(0.0, 0.0, 0.0);

    for(int i=0;i<controlVertices.size();i++)
    {

        float bernstein =CalculPolynomeBernstein(controlVertices.size()-1,i,t);

        vertex->x(vertex->x()+ bernstein*controlVertices[i]->x());
        vertex->y(vertex->y()+ bernstein*controlVertices[i]->y());
        vertex->z(vertex->z()+ bernstein*controlVertices[i]->z());
    }

    return vertex;
}


void Bezier::DeCasteljau(float t, std::vector<Vectorf*> sommets)
{
 /*   std::vector<Vectorf *> newVertices;

    glBegin(GL_LINE_STRIP);;
    {
        for(int i=0;i<sommets.size()-1;i++)
        {
            glVertex3f(sommets[i].x,sommets[i].y, sommets[i].z);

            Vector3f v = Vector3f::CalculVecteur(sommets[i],sommets[i+1]);
            v=v*t;
            v=v+sommets[i];
            newVertices.push_back(v);
        }

        glVertex3f(sommets[sommets.size()-1].x,sommets[sommets.size()-1].y, sommets[sommets.size()-1].z);
    }
    glEnd();

    if(newVertices.size()>1)
    {
        DeCasteljau(t, newVertices);
    }*/

}
